#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include "Thread.hpp"
// 线程池特点:
// 1. 线程池内部预先创建指定数量的线程,进行周期性的任务处理
// 2. 线程池内部维护一个任务队列,外部可以向线程池投放任务,线程池内部的线程从任务队列中获取任务
// 3. 当线程池内部的任务队列中没有任务,线程池内部的所有线程休眠
// 4. 一旦外部投放任务,唤醒一个或多个线程进行任务处理

// 解决如下问题
// 1. 线程预先创建,降低未来线程创建的成本！
// 2. 线程池内部线程总数是固定的,降低操作系统的压力！
using namespace std;
using namespace ThreadModule;
const static int default_threadnum = 3; // 默认线程池内部线程总数

template <typename T>
class ThreadPool
{
private:
    void LockQueue() // 任务队列加锁
    {
        pthread_mutex_lock(&(_mutex));
    }
    void UnLockQueue() // 任务队列解锁
    {
        pthread_mutex_unlock(&(_mutex));
    }

    void ThreadSleep() // 线程休眠(临界区中)
    {
        pthread_cond_wait(&(_cond), &(_mutex));
    }
    void ThreadWakeUp() // 线程唤醒
    {
        pthread_cond_signal(&(_cond));
    }

    void ThreadWakeUpAll() //唤醒所有线程
    {
        pthread_cond_broadcast(&(_cond));
    }

public:
    ThreadPool(int threadnum = default_threadnum)
        : _threadnum(threadnum), _isrunning(false)
    {
        pthread_mutex_init(&(_mutex), nullptr);
        pthread_cond_init(&(_cond), nullptr);
        _waitnum = 0;
    }

    // 线程池运行时
    // case 1:所有的线程全部在运行
    // case 2:一部分线程运行,一部分线程休眠
    // case 3:所有的线程全部在休眠
    void HandlerTask(string &threadname) // 处理任务
    {
        // 1.每一个线程都要从任务队列中获取任务,加锁保护任务队列
        while (true)
        {
            LockQueue();
            // 2.检测任务队列中是否存在任务&&线程是否正常运行
            while (_taskqueue.empty() && _isrunning)
            {
                // 任务队列为空,线程休眠
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }

            // 2.1 线程池已经退出 && 任务队列为空
            if (_taskqueue.empty() && !_isrunning)
            {
                UnLockQueue();
                cout<<threadname<<" quit ..."<<endl;
                break;
            }
            // 2.2 线程池正在运行 && 任务队列存在任务---正常获取任务
            // 2.3 线程池已经退出 && 任务队列存在任务---先处理完所有任务再退出
            // 3.获取任务(任务队列中数据类型T)
            T t = _taskqueue.front();
            _taskqueue.pop();

            // 4.解锁
            UnLockQueue();
            // 5.任务已经从任务队列中取出,此任务属于线程私有,处理任务
            // 注意:处理任务不能在加锁与解锁之间,任务已经取出,属于线程私有,可以提升处理效率
            //t();
        }
    }

    void InitThreadPool() // 初始化线程池
    {
        // 构建指定数量的线程并不启动&&将线程保存于vector中
        for (int num = 0; num < _threadnum; num++)
        {
            string threadname = "thread -" + to_string(num + 1);
            function<void(string &)> func = bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);
            Thread t(func, threadname);
            _threads.push_back(t);
            // 上述为将ThreadPool类的成员方法设置为Thread类的回调方法的常见做法
            //_threads.emplace_back(bind(&ThreadPool::Debug,this),threadname);
        }
        _isrunning = true;
    }
    void Start() // 启动线程池(统一启动所有线程,避免出现并发导致传参出错)
    {
        for (auto &thread : _threads)
        {
            thread.Start();
        }
    }

    void Wait() // 回收线程池内部所有线程
    {
        for (auto &thread : _threads)
        {
            thread.Join();
        }
    }

    bool EnQueue(const T &in) // 提供给外部投放任务
    {
        bool ret = false;
        // 外部线程向任务队列中投放任务,对任务队列进行加锁保护
        // 1.加锁
        LockQueue();
        // 2.1 保证在线程池启动的前提下,向任务队列中投放任务
        if (_isrunning)
        {
            // 2.投放任务
            _taskqueue.push(in);
            // 3.判断是否有线程在休眠,进行唤醒
            if (_waitnum > 0)
            {
                ThreadWakeUp();
            }
            ret = true;
        }
        UnLockQueue();
        return ret;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&(_mutex));
        pthread_cond_destroy(&(_cond));
    }

    void Stop()
    {
        LockQueue();
        _isrunning = false;
        // 唤醒等待队列中的所有线程
        ThreadWakeUpAll();
        UnLockQueue();
    }

private:
    int _threadnum;          // 线程池内部的线程总数
    vector<Thread> _threads; // 存放线程池内部的所有线程
    queue<T> _taskqueue;     // 线程池内部的任务队列
    // 线程池外部的线程向任务队列中投放任务,线程池内部的线程从任务队列中获取资源---任务队列为临界资源！！
    // 任务队列整体被使用,因此需要一把互斥锁对临界资源进行保护
    pthread_mutex_t _mutex;
    // 线程池内部的线程都需要从任务队列中获取任务,若任务队列为空,只能阻塞等待
    // 未来当外部投递任务,需要唤醒线程池的线程,因此需要条件变量
    pthread_cond_t _cond;

    int _waitnum; // 当前进行等待的线程数目

    bool _isrunning; // 线程池是否启动
};